﻿//---------------------------------------------------------------------------
// <copyright file="ReflectionExtensions.cs" company="P.O.S Informatique">
//     Copyright (c) P.O.S Informatique. All rights reserved.
// </copyright>
//---------------------------------------------------------------------------
namespace PosInformatique.Framework
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using PosInformatique.Framework;

    /// <summary>
    /// Contains extensions method of the Microsoft <c>System.Reflection</c> namespace.
    /// </summary>
    internal static class ReflectionExtensions
    {
        /// <summary>
        /// Gets the <typeparamref name="TAttribute"/> of the specified <paramref name="method"/> by escalading
        /// the following order if has not been found : method => class => assembly.
        /// </summary>
        /// <typeparam name="TAttribute">Attribute type to retrieve.</typeparam>
        /// <param name="method">Method which the attribute must be retrieve.</param>
        /// <returns>All attributes on the method, or the class of the method if no attribute has been found
        /// on the method, or the assembly of the class if no attribute has been found on the class.</returns>
        public static TAttribute[] GetPrioritizedCustomAttributes<TAttribute>(this MethodInfo method)
            where TAttribute : Attribute
        {
            ContractHelper.IsNotNull(method);
            ContractHelper.ReturnItemsNonNull<TAttribute>();

            TAttribute[] attributes;

            // Gets attributes in the method
            attributes = method.GetCustomAttributes<TAttribute>();

            if (attributes.Length == 0)
            {
                // Try to retrieve the attribute from the type of the method
                attributes = method.ReflectedType.GetCustomAttributes<TAttribute>();

                if (attributes.Length == 0)
                {
                    // Try to retrieve the attribute from the assembly of the type of the method
                    attributes = method.ReflectedType.Assembly.GetCustomAttributes<TAttribute>();
                }
            }

            return attributes;
        }

        /// <summary>
        /// Creates a new instance in the <paramref name="appDomain"/> of the specified <typeparamref name="T"/> type.
        /// </summary>
        /// <typeparam name="T">Object type to instantiate.</typeparam>
        /// <param name="appDomain"><see cref="AppDomain"/> where the <typeparamref name="T"/> type must be instantiated.</param>
        /// <param name="arguments">Constructor arguments of the <typeparamref name="T"/>.</param>
        /// <returns>A new instance of <typeparamref name="T"/> which has been instantiate in the <paramref name="appDomain"/>.</returns>
        public static T CreateInstanceAndUnwrap<T>(this AppDomain appDomain, params object[] arguments)
        {
            ContractHelper.IsNotNull(appDomain);

            return (T)appDomain.CreateInstanceAndUnwrap(
                typeof(T).Assembly.FullName,
                typeof(T).FullName,
                false,
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance,
                null,
                arguments,
                null,
                null);
        }

        /// <summary>
        /// Gets the <typeparamref name="TAttribute"/> of the specified reflection <paramref name="element"/> (method, field, class,...) 
        /// </summary>
        /// <typeparam name="TAttribute">Attribute type to retrieve.</typeparam>
        /// <param name="element">Reflection element to retrieve <typeparamref name="TAttribute"/>.</param>
        /// <returns>All the attribute of the specified reflection <paramref name="element"/>.</returns>
        private static TAttribute[] GetCustomAttributes<TAttribute>(this ICustomAttributeProvider element)
            where TAttribute : Attribute
        {
            ContractHelper.IsNotNull(element);
            ContractHelper.ReturnItemsNonNull<TAttribute>();

            return element.GetCustomAttributes(typeof(TAttribute), true).Cast<TAttribute>().ToArray();
        }
    }
}
